libobs_wrapper\data\output/
replay_buffer.rs1use std::{
7 path::{Path, PathBuf},
8 sync::Arc,
9};
10
11use crate::{
12 data::{
13 object::ObsObjectTrait,
14 output::{ObsOutputRef, ObsOutputTraitSealed},
15 },
16 forward_obs_object_impl, forward_obs_output_impl, impl_signal_manager, run_with_obs,
17 runtime::ObsRuntime,
18 unsafe_send::{Sendable, SmartPointerSendable},
19 utils::{ObsCalldataExt, ObsError, ObsString, OutputInfo},
20};
21
22#[derive(Debug, Clone)]
23pub struct ObsReplayBufferOutputRef {
31 replay_signal_manager: Arc<ObsReplayOutputSignals>,
33
34 output: ObsOutputRef,
35}
36
37impl ObsOutputTraitSealed for ObsReplayBufferOutputRef {
38 fn new(mut output: OutputInfo, runtime: ObsRuntime) -> Result<Self, ObsError> {
39 output.id = ObsString::new("replay_buffer");
40 let output = ObsOutputRef::new(output, runtime.clone())?;
41
42 let replay_signal_manager =
43 ObsReplayOutputSignals::new(&output.__native_handle(), runtime)?;
44 Ok(Self {
45 replay_signal_manager: Arc::new(replay_signal_manager),
46 output,
47 })
48 }
49
50 fn video_encoder_slot(
51 &self,
52 ) -> &std::sync::Arc<
53 std::sync::RwLock<Option<std::sync::Arc<crate::encoders::video::ObsVideoEncoder>>>,
54 > {
55 self.output.video_encoder_slot()
56 }
57
58 fn audio_encoder_slots(
59 &self,
60 ) -> &std::sync::Arc<
61 std::sync::RwLock<
62 std::collections::HashMap<
63 usize,
64 std::sync::Arc<crate::encoders::audio::ObsAudioEncoder>,
65 >,
66 >,
67 > {
68 self.output.audio_encoder_slots()
69 }
70
71 fn service_slot(
72 &self,
73 ) -> &std::sync::Arc<std::sync::RwLock<Option<std::sync::Arc<crate::services::ObsServiceRef>>>>
74 {
75 self.output.service_slot()
76 }
77
78 fn configuration_lock(&self) -> &std::sync::Arc<std::sync::Mutex<()>> {
79 self.output.configuration_lock()
80 }
81}
82
83forward_obs_object_impl!(ObsReplayBufferOutputRef, output, *mut libobs::obs_output);
84forward_obs_output_impl!(ObsReplayBufferOutputRef, output);
85
86impl_signal_manager!(|ptr: SmartPointerSendable<*mut libobs::obs_output>| {
87 unsafe {
88 libobs::obs_output_get_signal_handler(ptr.get_ptr())
90 }
91}, ObsReplayOutputSignals for *mut libobs::obs_output, [
92 "saved": {}
93]);
94
95impl ObsReplayBufferOutputRef {
96 pub fn replay_signals(&self) -> &Arc<ObsReplayOutputSignals> {
97 &self.replay_signal_manager
98 }
99 pub fn save_buffer(&self) -> Result<Box<Path>, ObsError> {
116 if self.runtime().is_actor_thread() {
117 return Err(ObsError::RuntimeReentrantBlocking);
118 }
119 log::trace!("Saving replay buffer...");
120 let output_ptr = self.__native_handle();
121
122 log::trace!("Getting procedure handler for replay buffer output...");
123 let proc_handler = run_with_obs!(self.runtime().clone(), (output_ptr), move || {
124 let ph = unsafe { libobs::obs_output_get_proc_handler(output_ptr.get_ptr()) };
126 if ph.is_null() {
127 return Err(ObsError::OutputSaveBufferFailure(
128 "Failed to get proc handler.".to_string(),
129 ));
130 }
131 Ok(Sendable(ph))
132 })??;
133
134 log::trace!("Calling 'save' procedure on replay buffer output...");
135 unsafe { self.runtime().call_proc_handler(&proc_handler, "save")? };
137
138 log::trace!("Waiting for 'saved' signal from replay buffer output...");
139 self.replay_signals()
140 .on_saved()?
141 .blocking_recv()
142 .map_err(|_e| {
143 ObsError::OutputSaveBufferFailure(
144 "Failed to receive saved replay buffer path.".to_string(),
145 )
146 })?;
147
148 log::trace!("Retrieving last replay path from replay buffer output...");
149 let mut calldata = unsafe {
151 self.runtime()
152 .call_proc_handler(&proc_handler, "get_last_replay")?
153 };
154
155 log::trace!("Extracting path from calldata...");
156 let path = calldata.get_string("path")?;
157 let path = PathBuf::from(path);
158
159 Ok(path.into_boxed_path())
160 }
161}